import React, { useCallback } from 'react';
import * as THREE from 'three';
import Stats from 'stats-js';
import * as dat from 'dat.gui';

export default function Index() {
  const refWrap = React.createRef();
  const refCanvas = React.createRef();
  const [camera, setCamera] = React.useState();
  const [renderer, setRenderer] = React.useState();
  // 动画帧数
  const initStats = () => {
    const stats = new Stats();
    stats.setMode(0);
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.left = '0';
    stats.domElement.style.top = '0';
    document.body.appendChild(stats.domElement);
    return stats;
  };

  const init = () => {
    const stats = initStats();
    refCanvas.current.width = refWrap.current.clientWidth;
    refCanvas.current.height = refWrap.current.clientHeight;
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(
      45,
      refCanvas.current.width / refCanvas.current.height,
      0.1,
      1000
    );
    const renderer = new THREE.WebGLRenderer({ canvas: refCanvas.current });
    setRenderer(renderer);
    renderer.setClearColor(0xeeeeee, 1.0);
    renderer.setSize(refCanvas.current.width, refCanvas.current.height);
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap

    const axes = new THREE.AxesHelper(20);
    scene.add(axes);

    const planGeometry = new THREE.PlaneGeometry(60, 20);
    const planMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
    const plan = new THREE.Mesh(planGeometry, planMaterial);
    plan.rotation.x = -1.5;
    plan.position.x = 10;
    plan.position.y = 0;
    plan.position.z = 0;
    plan.receiveShadow = true;
    scene.add(plan);

    const cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
    const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 });
    const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
    cube.position.x = -4;
    cube.position.y = 3;
    cube.position.z = 0;
    cube.castShadow = true;
    scene.add(cube);

    const sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
    const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0x7777ff });
    const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    sphere.position.x = 20;
    sphere.position.y = 4;
    sphere.position.z = 0;
    sphere.castShadow = true;
    scene.add(sphere);

    camera.position.x = -30;
    camera.position.y = 20;
    camera.position.z = 30;
    camera.lookAt(scene.position);
    setCamera(camera);

    // 添加光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-40, 60, -20);
    spotLight.castShadow = true;
    scene.add(spotLight);

    const controls = {
      rotationSpeed: 0.02,
      bouncingSpded: 0.03,
    };
    const gui = new dat.GUI();
    gui.add(controls, 'rotationSpeed', 0, 0.5);
    gui.add(controls, 'bouncingSpded', 0, 0.5);

    let step = 0;
    const reRender = () => {
      requestAnimationFrame(() => {
        stats.update();
        // 正方体旋转
        cube.rotation.x += controls.rotationSpeed;
        cube.rotation.y += controls.rotationSpeed;
        cube.rotation.z += controls.rotationSpeed;
        // 小球弹跳
        step += controls.bouncingSpded;
        sphere.position.x = 20 + 10 * Math.cos(step);
        sphere.position.y = 2 + 10 * Math.abs(Math.sin(step));

        renderer.render(scene, camera);
        reRender();
      });
    };

    reRender();
  };

  React.useEffect(() => {
    init();
  }, []);

  React.useEffect(() => {
    const onResize = () => {
      if (refWrap.current) {
        camera.aspect = refWrap.current.clientWidth / refWrap.current.clientHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(refWrap.current.clientWidth, refWrap.current.clientHeight);
      }
    };
    window.addEventListener('resize', onResize, false);
    return () => {
      window.removeEventListener('resize', onResize, false);
    };
  }, [camera]);

  return (
    <div ref={refWrap} style={{ width: '100%', height: '100%' }}>
      <canvas ref={refCanvas}></canvas>
    </div>
  );
}
